package com.xuecheng.orders.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.base.exception.XueChengPlusException;
import com.xuecheng.base.utils.IdWorkerUtils;
import com.xuecheng.base.utils.JsonUtil;
import com.xuecheng.base.utils.QRCodeUtil;
import com.xuecheng.messagesdk.model.po.MqMessage;
import com.xuecheng.messagesdk.service.MqMessageService;
import com.xuecheng.orders.config.AlipayConfig;
import com.xuecheng.orders.config.PayNotifyConfig;
import com.xuecheng.orders.mapper.XcOrdersGoodsMapper;
import com.xuecheng.orders.mapper.XcOrdersMapper;
import com.xuecheng.orders.mapper.XcPayRecordMapper;
import com.xuecheng.orders.model.dto.AddOrderDto;
import com.xuecheng.orders.model.dto.PayRecordDto;
import com.xuecheng.orders.model.dto.PayStatusDto;
import com.xuecheng.orders.model.po.XcOrders;
import com.xuecheng.orders.model.po.XcOrdersGoods;
import com.xuecheng.orders.model.po.XcPayRecord;
import com.xuecheng.orders.service.OrderService;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * @author: Hes
 * @description 保存商品订单实现
 * @date: 2023/6/27 17:24
 */
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    XcOrdersMapper xcOrdersMapper;
    @Autowired
    XcOrdersGoodsMapper xcOrdersGoodsMapper;
    @Autowired
    XcPayRecordMapper xcPayRecordMapper;

    @Value("${pay.qrcodeurl}")
    String qrcodeurl;

    @Autowired
    OrderServiceImpl currentProxy;

    @Value("${pay.alipay.APP_ID}")
    String APP_ID;
    @Value("${pay.alipay.APP_PRIVATE_KEY}")
    String APP_PRIVATE_KEY;

    @Value("${pay.alipay.ALIPAY_PUBLIC_KEY}")
    String ALIPAY_PUBLIC_KEY;

    @Autowired
    MqMessageService mqMessageService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 完成创建商品订单、创建支付交易记录
     *
     * @param userId
     * @param addOrderDto
     * @return
     */
    @Override
    @Transactional
    public PayRecordDto createOrder(String userId, AddOrderDto addOrderDto) {
        //添加商品订单
        XcOrders xcOrders = currentProxy.saveXcOrders(userId, addOrderDto);
        //添加支付交易记录
        XcPayRecord payRecord = currentProxy.createPayRecord(xcOrders);
        //生成二维码
        String codeUrl = String.format(qrcodeurl, payRecord.getPayNo());
        String qrCode = null;
        try {
            qrCode = new QRCodeUtil().createQRCode(codeUrl, 200, 200);

        } catch (IOException e) {
            log.debug("生成二维码失败,codeUrl:{}", codeUrl);
            XueChengPlusException.cast("生成二维码失败");
        }
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecord, payRecordDto);
        payRecordDto.setQrcode(qrCode);
        return payRecordDto;
    }

    /**
     * 扫码下单
     * 执行流程：
     * 1、用户输入支付密码，支付成功。
     * 2、接收第三方平台通知的支付结果。
     * 3、根据支付结果更新支付交易记录的支付状态为支付成功。
     *
     * @param payNo 交易记录流水号
     * @return
     */
    @Override
    public XcPayRecord getPayRecordByPayno(String payNo) {
        XcPayRecord xcPayRecord = xcPayRecordMapper.selectOne(new LambdaQueryWrapper<XcPayRecord>().eq(XcPayRecord::getPayNo, payNo));
        return xcPayRecord;
    }

    /**
     * 查询支付记录
     *
     * @param payNo 支付记录流水号
     * @return
     */
    @Override
    public PayRecordDto queryPayResult(String payNo) {

        XcPayRecord payRecordByPayno = getPayRecordByPayno(payNo);
        if (payRecordByPayno == null) {
            XueChengPlusException.cast("没有支付记录");
        }
        Long orderId = payRecordByPayno.getOrderId();
        XcOrders xcOrders = xcOrdersMapper.selectById(orderId);
        if (xcOrders == null) {
            XueChengPlusException.cast("没有订单");
        }
        String status = payRecordByPayno.getStatus();
        if ("601002".equals(status)) {
            log.debug("订单已支付");
            PayRecordDto payRecordDto = new PayRecordDto();
            BeanUtils.copyProperties(payRecordByPayno, payRecordDto);
            return payRecordDto;
        }
        //查询支付结果
        PayStatusDto payStatusDto = queryPayResultFromAlipay(payNo);
        //保存支付结果
        saveAliPayStatus(payStatusDto);
        XcPayRecord payRecord = getPayRecordByPayno(payNo);
        PayRecordDto payRecordDto = new PayRecordDto();
        BeanUtils.copyProperties(payRecord, payRecordDto);
        return payRecordDto;
    }

    /**
     * 保存支付结果
     *
     * @param payStatusDto
     */
    @Override
    @Transactional
    public void saveAliPayStatus(PayStatusDto payStatusDto) {
        String out_trade_no = payStatusDto.getOut_trade_no();
        XcPayRecord xcPayRecord = getPayRecordByPayno(out_trade_no);
        if (xcPayRecord == null) {
            log.debug("没有支付记录,payNo:{}", out_trade_no);
            XueChengPlusException.cast("没有支付记录");
        }
        String trade_status = payStatusDto.getTrade_status();
        if (!"TRADE_SUCCESS".equals(trade_status)) {
            XueChengPlusException.cast("支付结果查询失败");
        }
        //支付金额变为分
        Float totalPrice = xcPayRecord.getTotalPrice() * 100;
        Float total_amount = Float.parseFloat(payStatusDto.getTotal_amount()) * 100;
        //对账，账单总数
        if (!payStatusDto.getApp_id().equals(APP_ID) || totalPrice.intValue() != total_amount.intValue()) {
            //校验失败
            log.info("校验支付结果失败,支付记录:{},APP_ID:{},totalPrice:{}", xcPayRecord.toString(), payStatusDto.getApp_id(), total_amount.intValue());
            XueChengPlusException.cast("校验支付结果失败");
        }

        Long orderId = xcPayRecord.getOrderId();
        XcOrders xcOrders = xcOrdersMapper.selectById(orderId);
        if (xcOrders == null) {
            log.debug("没有订单,orderId:{},payNo:{}", orderId, out_trade_no);
            XueChengPlusException.cast("没有订单");
        }
        //更新支付记录
        xcPayRecord.setOutPayChannel("Alipay");
        xcPayRecord.setStatus("601002");
        xcPayRecord.setTotalPrice(Float.valueOf(payStatusDto.getTotal_amount()));
        xcPayRecord.setPaySuccessTime(LocalDateTime.now());
        //支付宝返回的支付号
        xcPayRecord.setOutPayNo(payStatusDto.getTrade_no());
        log.debug("支付记录更新，xcPayRecord:{}", xcPayRecord.toString());
        xcPayRecordMapper.updateById(xcPayRecord);
        //更新订单信息
        xcOrders.setStatus("600002");
        log.debug("订单信息更新，xcOrders:{}", xcOrders.toString());
        xcOrdersMapper.updateById(xcOrders);

        //保存消息记录,参数1：支付结果通知类型，2: 业务id，3:业务类型
        MqMessage payresult_notify = mqMessageService.addMessage(PayNotifyConfig.MESSAGE_TYPE, xcOrders.getOutBusinessId(), xcOrders.getOrderType(), null);
        //通知消息
        notifyPayResult(payresult_notify);
    }

    /**
     * 发送支付消息到交换机
     *
     * @param message
     */
    @Override
    public void notifyPayResult(MqMessage message) {
        //1、消息体，转json
        String msg = JSON.toJSONString(message);
        //设置消息持久化
        Message msgObject = MessageBuilder.
                withBody(msg.getBytes(StandardCharsets.UTF_8)).
                setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
        // 2.全局唯一的消息ID，需要封装到CorrelationData中
        CorrelationData correlationData = new CorrelationData(message.getId().toString());
        // 3.添加callback
        correlationData.getFuture().addCallback(
                result -> {
                    if (result.isAck()) {
                        // 3.1.ack，消息成功
                        log.debug("通知支付结果消息发送成功, ID:{}", correlationData.getId());
                        //删除消息表中的记录
                        mqMessageService.completed(message.getId());
                    } else {
                        // 3.2.nack，消息失败
                        log.error("通知支付结果消息发送失败, ID:{}, 原因{}", correlationData.getId(), result.getReason());
                    }
                }
                , ex -> log.error("消息发送异常, ID:{}, 原因{}", correlationData.getId(), ex.getMessage())
        );
        // 发送消息 参数（交换机，路由模式，消息，CorrelationData对象）
        rabbitTemplate.convertAndSend(PayNotifyConfig.PAYNOTIFY_EXCHANGE_FANOUT, "", msgObject, correlationData);
    }

    /**
     * 查询支付结果
     *
     * @throws AlipayApiException
     */
    public PayStatusDto queryPayResultFromAlipay(String payNo) {
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, APP_ID, APP_PRIVATE_KEY, "json", AlipayConfig.CHARSET, ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE); //获得初始化的AlipayClient
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", payNo);
        //bizContent.put("trade_no", "2014112611001004680073956707");
        request.setBizContent(bizContent.toString());
        AlipayTradeQueryResponse response = null;
        try {
            response = alipayClient.execute(request);
        } catch (AlipayApiException e) {
            XueChengPlusException.cast("调用失败");
        }
        if (!response.isSuccess()) {
            XueChengPlusException.cast("调用失败");
        }
        log.debug("调用成功");
        PayStatusDto payStatusDto = new PayStatusDto();
        String resultJson = response.getBody();
        //转map
        Map resultMap = JSON.parseObject(resultJson, Map.class);
        Map alipay_trade_query_response = (Map) resultMap.get("alipay_trade_query_response");
        //支付结果
        String trade_status = (String) alipay_trade_query_response.get("trade_status");
        String trade_no = (String) alipay_trade_query_response.get("trade_no");
        String total_amount = (String) alipay_trade_query_response.get("total_amount");
        payStatusDto.setTrade_status(trade_status);
        //商户订单号
        payStatusDto.setOut_trade_no(payNo);
        payStatusDto.setTrade_no(trade_no);
        payStatusDto.setTotal_amount(total_amount);
        payStatusDto.setApp_id(APP_ID);
        return payStatusDto;
    }

    /**
     * 添加支付记录
     *
     * @param xcOrders
     * @return
     */
    @Transactional
    public XcPayRecord createPayRecord(XcOrders xcOrders) {
        if (xcOrders == null) {
            XueChengPlusException.cast("订单不存在");
        }
        if (!xcOrders.getStatus().equals("600001")) {
            XueChengPlusException.cast("订单已支付或已退款");
        }
        XcPayRecord xcPayRecord = new XcPayRecord();
        //生成订单记录流水号
        long payNo = IdWorkerUtils.getInstance().nextId();
        xcPayRecord.setPayNo(payNo);
        xcPayRecord.setOrderId(xcOrders.getId());
        xcPayRecord.setCreateDate(LocalDateTime.now());
        xcPayRecord.setOrderName(xcOrders.getOrderName());
        xcPayRecord.setOutPayNo(xcPayRecord.getOutPayNo());
        xcPayRecord.setStatus("601001");
        xcPayRecord.setTotalPrice(xcOrders.getTotalPrice());
        xcPayRecord.setUserId(xcOrders.getUserId());
        xcPayRecord.setCurrency("CNY");
        xcPayRecordMapper.insert(xcPayRecord);
        return xcPayRecord;
    }

    /**
     * 添加订单信息
     *
     * @param userId
     * @param addOrderDto
     * @return
     */
    @Transactional
    public XcOrders saveXcOrders(String userId, AddOrderDto addOrderDto) {
        //幂等性处理
        XcOrders xcOrder = getOrderByBusinessId(addOrderDto.getOutBusinessId());
        if (xcOrder != null) {
            return xcOrder;
        }
        xcOrder = new XcOrders();
        //创建订单id
        long id = IdWorkerUtils.getInstance().nextId();//雪花算法生成id
        xcOrder.setOrderDetail(addOrderDto.getOrderDetail());
        xcOrder.setOrderName(addOrderDto.getOrderName());
        xcOrder.setOrderType(addOrderDto.getOrderType());
        xcOrder.setOutBusinessId(addOrderDto.getOutBusinessId());
        xcOrder.setUserId(userId);
        xcOrder.setStatus("600001");
        xcOrder.setCreateDate(LocalDateTime.now());
        xcOrder.setTotalPrice(addOrderDto.getTotalPrice());
        xcOrder.setId(id);
        xcOrder.setOrderDescrip(addOrderDto.getOrderDescrip());
        xcOrdersMapper.insert(xcOrder);
        //插入订单明细表
        String orderDetail = xcOrder.getOrderDetail();
        //转集合
        List<XcOrdersGoods> xcOrdersGoods = JSON.parseArray(orderDetail, XcOrdersGoods.class);
        xcOrdersGoods.forEach(xcOrdersGood -> {
            XcOrdersGoods ordersGoods = new XcOrdersGoods();
            BeanUtils.copyProperties(xcOrdersGood, ordersGoods);
            ordersGoods.setOrderId(id);
            xcOrdersGoodsMapper.insert(ordersGoods);
        });
        return xcOrder;
    }

    /**
     * 根据业务id查询订单
     *
     * @param businessId
     * @return
     */
    public XcOrders getOrderByBusinessId(String businessId) {
        XcOrders xcOrders = xcOrdersMapper.selectOne(new LambdaQueryWrapper<XcOrders>().eq(XcOrders::getOutBusinessId, businessId));
        return xcOrders;
    }
}
